﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;

namespace alphabook
{
    class Loan
    {
        private int id = -1;

        private User loaner;
        public User Lender
        {
            get { return loaner; }
        }

        private Book lentbook;
        public Book LentBook
        {
            get { return lentbook; }
        }

        private DateTime outdate;
        public DateTime OutDate
        {
            get { return outdate; }
            set { outdate = value; Update(); }
        }

        private DateTime? indate;
        public DateTime? InDate
        {
            get { return indate; }
            set { indate = value; Update(); }
        }

        private DateTime expectedin;
        public DateTime ExpectedIn
        {
            get { return expectedin; }
            set { expectedin = value; Update(); }
        }


        /// <summary>
        /// Loan empty
        /// </summary>
        public Loan()
        {
        }

        /// <summary>
        /// Lend a new book
        /// </summary>
        /// <param name="loaner">User who loans</param>
        /// <param name="lentbook">Loaned book</param>
        /// <param name="expectedin">Expected return date</param>
        public Loan(User loaner, Book lentbook, DateTime expectedin)
        {
            this.loaner = loaner;
            this.lentbook = lentbook;
            this.outdate = DateTime.Now;
            this.expectedin = expectedin;
            this.Insert();
        }

        /// <summary>
        /// Lend a new book
        /// </summary>
        /// <param name="loaner">User who loans</param>
        /// <param name="lentbook">Loaned book</param>
        /// <param name="outdate">Lending date</param>
        /// <param name="expectedin">Expected return date</param>
        public Loan(User loaner, Book lentbook, DateTime outdate, DateTime expectedin)
        {
            this.loaner = loaner;
            this.lentbook = lentbook;
            this.outdate = outdate;
            this.expectedin = expectedin;
            this.Insert();
        }

        /// <summary>
        /// Create an existing loan
        /// </summary>
        /// <param name="id">Loan ID</param>
        /// <param name="loaner">User who loans</param>
        /// <param name="lentbook">Loaned book</param>
        /// <param name="outdate">Lending date</param>
        /// <param name="expectedin">Expected return date</param>
        public Loan(int id, User loaner, Book lentbook, DateTime outdate, DateTime? indate, DateTime expectedin)
        {
            this.id = id;
            this.loaner = loaner;
            this.lentbook = lentbook;
            this.outdate = outdate;
            if (indate != null)
            {
                this.indate = indate;
            }
            this.expectedin = expectedin;
        }

        /// <summary>
        /// Update loan values
        /// </summary>
        /// <returns>0 - success, 1 - failed</returns>
        private int Update()
        {
            Database db = new Database();
            if (this.id == -1)
            {
                return 0;
            }
            String sql = "UPDATE BOOK_OUT SET book_out = '" + this.outdate +
            "', book_in = " + this.indate + ", expected_in = '" + this.expectedin +
            " WHERE book_out_ID = " + this.id;
            int rows = db.Execute(sql);
            db.Close();
            return Util.checkResult(rows);

        }

        /// <summary>
        /// Saves the loan to database
        /// </summary>
        /// <returns>0 - success, -1 - book exists, 1 - other error</returns>
        private int Insert()
        {
            Database db = new Database();
            int rows = db.Execute("INSERT INTO BOOK_OUT VALUES(null, " + this.lentbook.ID + ", " +
                this.loaner.ID + ", '" + Util.toMySQLDate(this.outdate) + "', null, '" + Util.toMySQLDate(this.expectedin) + "');");
            ArrayList id = db.Query("SELECT * FROM BOOK_OUT WHERE book_ID = " + this.lentbook.ID + " AND book_in IS NULL;");
            Dictionary<String, object> d = (Dictionary<String, object>)id[0];
            this.id = (int)d["book_out_ID"];
            db.Close();
            return Util.checkResult(rows);

        }

        /// <summary>
        /// Fetches all loans
        /// </summary>
        /// <returns>Array of loans</returns>
        public static Loan[] GetAllLoans()
        {
            Database db = new Database();

            ArrayList loans = db.Query("SELECT * FROM BOOK_OUT;");
            Loan[] result = new Loan[loans.Count];

            for (int i = 0; i < loans.Count; i++)
            {
                Dictionary<String, object> dLoan = (Dictionary<string, object>)loans[i];

                User lUser = User.getUser((int)dLoan["user_ID"]);
                Book lBook = Book.getBook((int)dLoan["user_ID"]);
                DateTime? book_in = (dLoan["book_in"].ToString() == "") ? null : (DateTime?)dLoan["book_in"];

                result[i] = new Loan((int)dLoan["book_out_ID"], lUser, lBook, (DateTime)dLoan["book_out"],
                    book_in, (DateTime)dLoan["expected_in"]);
            }

            return result;
        }

        /// <summary>
        /// Fetches overdue loans
        /// </summary>
        /// <returns>Array of loans</returns>
        public static Loan[] GetOverdueLoans()
        {
            Database db = new Database();

            ArrayList loans = db.Query("SELECT * FROM BOOK_OUT WHERE book_in IS NULL AND book_out > expected_in;");
            Loan[] result = new Loan[loans.Count];

            for (int i = 0; i < loans.Count; i++)
            {
                Dictionary<String, object> dLoan = (Dictionary<string, object>)loans[i];

                User lUser = User.getUser((int)dLoan["user_ID"]);
                Book lBook = Book.getBook((int)dLoan["book_ID"]);
                DateTime? book_in = (dLoan["book_in"].ToString() == "") ? null : (DateTime?)dLoan["book_in"];

                result[i] = new Loan((int)dLoan["book_out_ID"], lUser, lBook, (DateTime)dLoan["book_out"],
                    book_in, (DateTime)dLoan["expected_in"]);
            }
            return result;
        }


        /// <summary>
        /// Returns the book
        /// </summary>
        /// <returns>0 - success, 1 - failed</returns>
        public static int ReturnBook(Book b, User u)
        {
            Database db = new Database();

            String sql = "UPDATE BOOK_OUT SET book_in = '" + Util.toMySQLDate(DateTime.Now)
                + "' WHERE book_ID = " + b.ID + " AND user_ID = " + u.ID + " AND book_in IS NULL;";
            int rows = db.Execute(sql);
            return Util.checkResult(rows);
        }

        /// <summary>
        /// Fetches active loans
        /// </summary>
        /// <returns>Array of loans</returns>
        public static Loan[] GetActiveLoans()
        {
            Database db = new Database();

            ArrayList loans = db.Query("SELECT * FROM BOOK_OUT WHERE book_in IS NULL;");
            Loan[] result = new Loan[loans.Count];

            for (int i = 0; i < loans.Count; i++)
            {
                Dictionary<String, object> dLoan = (Dictionary<string, object>)loans[i];

                User lUser = User.getUser((int)dLoan["user_ID"]);
                Book lBook = Book.getBook((int)dLoan["book_ID"]);
                DateTime? book_in = (dLoan["book_in"].ToString() == "") ? null : (DateTime?)dLoan["book_in"];

                result[i] = new Loan((int)dLoan["book_out_ID"], lUser, lBook, (DateTime)dLoan["book_out"],
                    book_in, (DateTime)dLoan["expected_in"]);
            }
            return result;
        }

        /// <summary>
        /// Fetches loaned out books by user
        /// </summary>
        /// <param name="id">User ID</param>
        /// <returns>Array of Books</returns>
        public static Loan[] GetLoansByUser(int id)
        {
            Database db = new Database();
            ArrayList loans = db.Query("SELECT * FROM BOOK_OUT WHERE user_ID = " + id + ";");
            Loan[] result = new Loan[loans.Count];
            for (int i = 0; i < loans.Count; i++)
            {
                Dictionary<String, object> dLoan = (Dictionary<string, object>)loans[i];

                User lUser = User.getUser((int)dLoan["user_ID"]);
                Book lBook = Book.getBook((int)dLoan["user_ID"]);
                DateTime? book_in = (dLoan["book_in"].ToString() == "") ? null : (DateTime?)dLoan["book_in"];

                result[i] = new Loan((int)dLoan["book_out_ID"], lUser, lBook, (DateTime)dLoan["book_out"],
                    book_in, (DateTime)dLoan["expected_in"]);
            }
            return result;
        }
    }
}
